#include "CPhpCounter.h"

CPhpCounter::CPhpCounter()
{
	classtype = PHP;
	language_name = "PHP";

	file_extension.push_back(".*php");

	directive.push_back("import");
	directive.push_back("include");
	directive.push_back("include_once");
	directive.push_back("require");
	directive.push_back("require_once");
	directive.push_back("declare");
	directive.push_back("define");

	QuoteStart = "\"";
	QuoteEnd = "\"";
	QuoteEscapeFront = '\\';
	ContinueLine = '\\';
	BlockCommentStart.push_back("/*");
	BlockCommentEnd.push_back("*/");

	LineCommentStart.push_back("//");
	LineCommentStart.push_back("#");

	flow_control_keywords.push_back("if");
	flow_control_keywords.push_back("for");
	flow_control_keywords.push_back("while");
	flow_control_keywords.push_back("foreach");

	data_name_list.push_back("class");
	data_name_list.push_back("function");
	data_name_list.push_back("global");
	data_name_list.push_back("array");
	data_name_list.push_back("public");
	data_name_list.push_back("private");
	data_name_list.push_back("protected");
	data_name_list.push_back("var");
	data_name_list.push_back("echo");
	data_name_list.push_back("print");
	data_name_list.push_back("bool");
	data_name_list.push_back("int");
	data_name_list.push_back("float");
	data_name_list.push_back("string");
	data_name_list.push_back("const");
	data_name_list.push_back("object");
	data_name_list.push_back("NULL");
	data_name_list.push_back("interface");
	data_name_list.push_back("extends");

	exec_name_list.push_back("throw");
	exec_name_list.push_back("Exception");
	exec_name_list.push_back("return");
	exec_name_list.push_back("this");
	exec_name_list.push_back("try");
	exec_name_list.push_back("catch");      
	exec_name_list.push_back("new");
	exec_name_list.push_back("if");
	exec_name_list.push_back("else");
	exec_name_list.push_back("for");
	exec_name_list.push_back("foreach");
	exec_name_list.push_back("do");
	exec_name_list.push_back("while");
	exec_name_list.push_back("continue");
	exec_name_list.push_back("switch");
	exec_name_list.push_back("case");
	exec_name_list.push_back("default");
	exec_name_list.push_back("break");
	exec_name_list.push_back("die");      
	exec_name_list.push_back("isset"); 
	exec_name_list.push_back("exit");
}

int CPhpCounter::CountDirectiveSLOC(filemap* fmap, results* result, filemap* fmapBak)
{
	bool contd = false;
	size_t idx;
	if(classtype == DATAFILE) return 0;
//	int cnt = 0;
	string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$";

	for (filemap::iterator iter = fmap->begin(); iter!=fmap->end(); iter++)
	{
		if (CUtil::CheckBlank(iter->line)) continue;

		int cnt=0;
		CUtil::CountTally(" " + iter->line, directive, cnt, 1, exclude, "", "", &directive_count, result->duplicate);

		if (!contd)
		{	// if not a continuation of a previous directive
			for(vector<string>::iterator viter = directive.begin(); viter != directive.end(); viter++)
			{
				if ((idx = iter->line.find((*viter), 0)) != string::npos && idx == 0)
				{
					contd = true;
					break;
   		        }
        	}
			if (contd)
			{
				result->directive_lines[PHY]++;				
			}	// incrementing the physical directive lines
 		}
		else
		{	// continuation of a previous directive
			result->directive_lines[PHY]++;	// incrementing the physical lines for a directive
		}
	
		if (contd)
		{	// if a directive or continuation of a directive
			if (iter->line[iter->line.length()-1] != ',' && iter->line[iter->line.length()-1] != '\\')
			{
				contd = false;
				result->directive_lines[LOG]++;
			}	// if no continuation symbol found
			iter->line = "";
		}
	}
	return 1;
}

// Special Processing(w/ some revision to logic)
// Data Processing(works the same as before)
// Exec Processing(works the same as before)
// IMPORTANT NOTE: all the  blank lines +
//							whole line comments +
//							line with compiler directive
//				   should have been blanked from filemap by previous processing
//                 before reaching this function
int CPhpCounter::LanguageSpecificProcess(filemap* fmap, results* result, filemap* fmapBak)
{
	bool		blank_flag			= false;
	bool		begin_paren_flag	= false;
	bool		end_paren_flag		= false;
	int			paren_count			= 0	   ;
	bool		data_flag			= false;
	bool		data_continue		= false;
	bool		found_brace			= false;
	int			num_FOR_IF_WHILE	= 0	   ;
	int			num_terminators		= 0	   ;
	bool		SLOC_end_flag		= false;
	bool		for_flag			= false;

	filemap::iterator fit;
	string line, tmp;

	// two strings used for string match
	string  exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$";
	string	special = "[]()+/-*<>=,@&~!^?:%{}";

	//********
//	int lineIndex=1;
	//********
	for (fit = fmap->begin(); fit != fmap->end(); fit++)
	{
		line = fit->line;

		if (CUtil::CheckBlank(line))
		{
			blank_flag = true;// the line is either blank/whole line comment/compiler directive
		}
		else
		{
			blank_flag = false;// should be processed by this func
		}

		// insert blank at the beginning(for searching keywords
		line = ' ' + line;
	
		/*****************************************************************************/
		/*                                                                           */
		/* Special processing:                                                       */
		/*   - determine the number of '}' statement terminators per physical line.  */
		/*   - determine the number of ';' statement terminators per physical line.  */
		/*   - don't count '}' followed immediately by a ';'.                        */
		/*   - don't count ';'s inside of parenthesis, e.g. for conditionals.        */
		/*   - increment number of statement terminators due to occurrences of FOR,  */
		/*      IF and WHILE statements that do not terminate with a '}'.            */
		/*   - blank characters from 'line' that are contained in 'special'.         */
		/*                                                                           */
		/* Notes:                                                                    */
		/*   - FOR, IF, and WHILE keywords can be followed by a left paren.          */
		/*                                                                           */
		/*****************************************************************************/
		if (!blank_flag)
		{	// blank line means blank_line/comment_line/directive 

			// Count all FOR, IF, WHILE until valid ';' is encountered
			count_special(line, num_FOR_IF_WHILE);
		
			for (size_t i=0; i<line.size(); i++)
			{
				if (line[i] == '(')						// Used to terminate data
		   		{										//  declar. in arg. lists
					begin_paren_flag = true;
					if (for_flag)
					{
						paren_count++;
					}
					else
					{
						tmp = "xxx " + CUtil::TrimString(line.substr(0,i));
						tmp = CUtil::TrimString(tmp.substr(tmp.length()-4,4));
						if (tmp.find_first_of("{}:;")==0) tmp = tmp.substr(1,3);
						if (tmp == "for")
						{
							for_flag = true;
							paren_count++;
						}
					}

					//begin_paren_flag = true;
					//paren_count++;
		   		}
	 
		   		if (line[i] == ')')						// Used to terminate data
		   		{										//  declar. in arg. lists
					end_paren_flag = true;
					if (for_flag)
					{
						paren_count--;
						if (paren_count == 0) for_flag = false;
					}

					//end_paren_flag = true;
					//paren_count--;
				}
	 
		   		if (line[i] == ';')						// Process ';' not within ()s
				{
					if (paren_count ==  0)
		 			{
			   			if (found_brace == false)
			   			{								// Will not update a logical
							SLOC_end_flag = true;		// SLOC when '}' is followed
				 			num_terminators++;			// by a ';'.

				 			if (num_FOR_IF_WHILE >= 0)	// All previous FOR, IF, or
				 			{							// WHILE statements did not
														// have a ';' or '}' logical
														// SLOC terminator.
								result->exec_lines[LOG] += num_FOR_IF_WHILE;

				   				num_FOR_IF_WHILE = 0;	// Reset for next occurrence.
														// count for,if,while after ;
				   				count_special(line.substr(i+1), num_FOR_IF_WHILE);
				 			}
							else
							{							// Error, can't be < 0.
					 			cerr << "Error: num_FOR_IF_WHILE <0\n";
								result->e_flag = true;
								result->error_code = "Error during special processing";
								return 0;
							}
			   			}
					}
					else
					{
						line[i] = ' ';					// Blank ';' in FOR construct
					}
				}

				if (found_brace == true)				// Continue processing '}'
				{										// by examining next char.
					if (line[i] == ',')
					{
						SLOC_end_flag = false;			// Continuation of current
						found_brace   = false;			// line, '}' is not a
						num_terminators--;				// logical SLOC terminator.
					}

					if (line[i] != ' ')
					{									// '}' is a logical SLOC
						found_brace = false;			// terminator, expect to
					}									// detect more ';'s.
				}

				if (line[i] == '}')
				{										// Logical SLOC terminator
					if (num_FOR_IF_WHILE > 0)
					{
						result->exec_lines[LOG] += num_FOR_IF_WHILE;
						num_FOR_IF_WHILE = 0;
						count_special(line.substr(i+1), num_FOR_IF_WHILE);
					}

					SLOC_end_flag = true;
					found_brace   = true;
					num_terminators++;
				}

				if ((line[i] == '{') &&					// Block for FOR, IF, or
					(num_FOR_IF_WHILE > 0 ))			// WHILE will be processed
				{
					num_FOR_IF_WHILE--;					// when '}' is detected.
				}

                if(special.find_first_of(line[i]) != string::npos) 
				{
					line[i] = ' ';
				}

			} //for
		} //if(not a blank)

		/*****************************************************************************/
		/*                                                                           */
		/* Data Line processing:                                                     */
		/*   - use DATA_NAME_LIST to determine that a data line has been detected.   */
		/*   - declare SLOC to be a data line accordingly.                           */
		/*   - increment physical line count.                                        */
		/*   - use previous detection of a terminal ';' or '}' to indicate           */
		/*      termination of the data line.                                        */
		/*   - increment logical line count accordingly.                             */
		/*                                                                           */
		/* Notes:                                                                    */
		/*   - default is an executable line.                                        */
		/*                                                                           */
		/*****************************************************************************/
		if (!blank_flag)
		{
														// data_continue is true only
														// during processing of
														// data declarations.
			if ((data_flag == true ) && (data_continue == false))
			{
				data_flag = false;						// Reset data_flag
			}
			int cnt=0;
			CUtil::CountTally(line, data_name_list, cnt, 1, exclude, "", "", &data_name_count, result->duplicate);

			if ((cnt > 0) || (data_continue == true))
			{
				data_flag = true;
			}
            else
			{
				data_flag = false;
			}

			if (data_flag == true)
			{											// If data declaration
				result->data_lines[PHY]++;				// Update physical SLOC lines
				if (SLOC_end_flag == true)
				{
														// Update logical SLOC lines
					result->data_lines[LOG] += num_terminators;
					SLOC_end_flag   = false;			// Reset for next SLOC line
					num_terminators =    0 ;
					data_continue   = false;
				}
				else
				{
					data_continue   = true ;
				}
			}											// If data declaration
		}	// if(not a blank)
 
		/*****************************************************************************/
		/*                                                                           */
		/* Executable Line processing:                                               */
		/*   - default is an executable line.                                        */
		/*   - declare SLOC to be a executable line accordingly.                     */
		/*   - increment physical line count.                                        */
		/*   - use previous detection of a terminal ';' or '}' to indicate           */
		/*      termination of the data line.                                        */
		/*   - increment logical line count accordingly.                             */
		/*   - count keywords in EXEC_NAME_LIST.                                     */
		/*                                                                           */
		/* Notes:                                                                    */
		/*   -                                                                       */
		/*                                                                           */
		/*****************************************************************************/

		int cnt=0;
		CUtil::CountTally(line, exec_name_list, cnt, 1, exclude, "", "", &exec_name_count, result->duplicate);

		if (!blank_flag && data_flag == false)
		{
			result->exec_lines[PHY]++;					// Update physical SLOC lines
			if (SLOC_end_flag == true)
			{
														// Update logical SLOC lines
				result->exec_lines[LOG] += num_terminators;
				SLOC_end_flag   = false;				// Reset for next SLOC line
				num_terminators =     0;
			}
		}	// if(not a blank && !data_flag)

		/*****************************************************************************/
		/* Fault Tolerance against													 */
		/*  misclassification of													 */
		/*  data_lines following end												 */
		/*  of a function argument list												 */
		/*****************************************************************************/

		if ((begin_paren_flag == true) &&
			(  end_paren_flag == true) &&
			(paren_count      == 0   ))
		{
			begin_paren_flag = false;					// Reset, found final
			end_paren_flag   = false;					// matching set.
 
			if (!CUtil::CheckBlank(line))
			{
				if((data_flag == true) && (data_continue  == true))
				{										// Reset BOOLEANS
					data_continue      = false;
				}										// Reset BOOLEANS
			}
       }

		// Temporary code, please update
		LSLOC(result, line);

	}	//for(fit)
	return 1;
}

// Method: LSLOC(...)
// Description: determine and extract logical SLOC to place in the result variable
//              using addSLOC function. Each time the addSLOC function is called,
//              a new logical SLOC is added. This function assumes that the directive
//              is handled before it is called.
// -----------------------------------------
// Revision  Date     Author  Modification
// Rev-1     4/19/09   VN      Created the method signature (frame).
//								Note:the parameters list must be added
void CPhpCounter::LSLOC(results* result, string line)
{
	// The following line is test-only, please implement the method to make it work correctly
	bool trunc_flag = false;
	result->addSLOC(line, trunc_flag);
}

// Count all FOR, IF, WHILE until valid statement terminator is encountered
//   OR, if not found, until the end of line
// - not for generic use
void CPhpCounter::count_special(string base, int& count)
{
	size_t i;
	int paren_cnt = 0;
	bool brace = false;
	bool found = false;
	bool forflag = false;
	string::size_type idx;
	string tmp;

	// find first valid stmt terminator
	// ';' (excluding ';' after '}' )
	//     (excluding ';' inside () )
	// '}'
	// and chop base
	for (i=0; i<base.size();i++)
	{
		switch (base[i])
		{
		case ';':
			if (paren_cnt > 0)
				break;
			if (!brace) found = true; 
			break;
		case '}':
			brace = true;
			break;
		case ' ': case '\t':
			break;
		case '(':
			if (forflag)
			{
				paren_cnt++;
			}
			else
			{
				tmp = "xxx " + CUtil::TrimString(base.substr(0,i));
				tmp = CUtil::TrimString(tmp.substr(tmp.length()-4,4));
				if (tmp.find_first_of("{}:;")==0) tmp = tmp.substr(1,3);
				if (tmp == "for")
				{
					forflag = true;
					paren_cnt++;
				}
			}
			break;
		case ')':
			if (forflag)
			{
				paren_cnt--;
				if (paren_cnt == 0) forflag = false;
			}
			break;
		default:
			brace = false;
		}
		if (found) break;
	}
	idx = base.find("}");
	if (idx != string::npos)
	{
		if (idx < i)
			base = base.substr(0,idx);
		else
			base = base.substr(0,i);
	}
	else
	{
		base = base.substr(0,i);
	}

	// count entries in the srch[]
	// entires do not overlap so we can
	// count them separately


	string inc1 = " \t{};)";		// set of characters that can come before keywords
	string inc2 = " \t(\n\f\r";
	//count_tally(base, for_if_while, 3, count, 2, "", inc1, inc2);
	CUtil::CountTally(base, flow_control_keywords, count, 2, "", inc1, inc2);
}
